package com.zy.convert.core;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zy.convert.converter.Transform;
import com.zy.convert.constant.ConvertConstant;
import com.zy.convert.util.ReflectHelper;
import com.zy.convert.exception.ConvertParamException;
import com.zy.convert.exception.ConvertTypeException;
import org.apache.commons.lang3.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.logging.Logger;

/**
 * @author hassan
 * @date 2020-02-24 23:14
 * 属性转换执行器
 */
public class ConvertExecutor<T>{
    private final static Logger logger = Logger.getLogger(ConvertExecutor.class.getName());

    /**
     * 空转换器
     */
    private final static ConvertExecutor<?> CONVERT_NO = new ConvertExecutor<>(Object.class);
    /**
     * 执行器缓存
     */
    private final static Map<Class<?>, ConvertExecutor<?>> CACHE_CONVERT_EXECUTOR = new HashMap<>();
    /**
     * 转换的目标类
     */
    private final Class<T> targetClass;
    /**
     * targetClass中可以转换的字段Converter信息
     */
    private final List<ConverterFieldInfo> cacheConverterFieldInfoList = new ArrayList<>();

    public ConvertExecutor(Class<T> targetClass) {
        if (targetClass == null) {
            throw new NullPointerException("ConvertExecutor could not instantiate without targetClass");
        }
        this.targetClass = targetClass;
        init();
    }

    /**
     * 获取 对象转换执行器
     * @param targetClass 目标类型 / 转换结果类型
     * @return 返回执行器
     */
    public static <T> ConvertExecutor getExecutor(Class<T> targetClass) {
        if (targetClass == null) {
            return CONVERT_NO;
        }
        // 双重验证，并缓存
        if (!CACHE_CONVERT_EXECUTOR.containsKey(targetClass)) {
            synchronized (ConvertExecutor.class) {
                if (!CACHE_CONVERT_EXECUTOR.containsKey(targetClass)) {
                    ConvertExecutor convertExecutor = new ConvertExecutor<>(targetClass);
                    CACHE_CONVERT_EXECUTOR.put(targetClass, convertExecutor);
                }
            }
        }
        return CACHE_CONVERT_EXECUTOR.get(targetClass);
    }

    public <S> T convert(S source, Class<?>... converterAnnotationClasses) {
        if (source == null) {
            return null;
        }
        Class<?> sourceClass = source.getClass();
        long beforeTime = System.currentTimeMillis();
        // 使用JSONObject转化对象
        T target = JSONObject.parseObject(JSONObject.toJSONString(source), targetClass);
        // 处理attach，保证此次转换一直能拿到这个 attach
        Map<String, Object> attach=new HashMap<>();
        // 过滤出此次需要转换的信息
        List<ConverterFieldInfo> alternativeConvertInfoList = alternativeConvertInfoList(converterAnnotationClasses);
        for (ConverterFieldInfo converterFieldInfo : alternativeConvertInfoList) {
            try {
                String sourceFieldName = converterFieldInfo.getSourceFieldName();
                String sourceFieldGetterName = ReflectHelper.field2getter(sourceFieldName);
                Method sourceFieldGetter = sourceClass.getMethod(sourceFieldGetterName);
                Object sourceValue = sourceFieldGetter.invoke(source);
                // 封装 convertContext
                ConvertContext convertContext = new ConvertContext<>();
                convertContext.setAttach(attach);
                convertContext.setConverterAnnotation(converterFieldInfo.getConverterAnnotation());
                convertContext.setSourceValue(sourceValue);

                // 执行转换
                AbstractConverter converter = converterFieldInfo.getConverter();
                Object targetFieldValue = converter.convert(convertContext);

                // 对target设置结果值
                setTargetValue(target,targetFieldValue, converterFieldInfo);
                // 处理attach，保证此次转换一直能拿到这个 attach
                attach=convertContext.getAttach();
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ConvertTypeException e) {
                logger.info(e.getClass().getSimpleName() + " : " + e.getMessage());
            }
        }
        long afterTime = System.currentTimeMillis();
        long executeTime=(afterTime- beforeTime)/100;
        System.out.println("执行转换 [ "+targetClass.getSimpleName()+" ] , 消耗时间："+executeTime);
        return target;
    }

    public <S> List<T> convertList(List<S> sourceList, Class<?>... converterAnnotationClasses) {
        if (sourceList == null || sourceList.size() == 0) {
            return new ArrayList<>();
        }
        Class<?> sourceClass = sourceList.get(0).getClass();
        long beforeTime = System.currentTimeMillis();
        // 使用JSONObject转化对象
        List<T> targetList = JSONArray.parseArray(JSONArray.toJSONString(sourceList), targetClass);

        // 处理attach，保证此次转换一直能拿到这个 attach
        Map<String, Object> attach=new HashMap<>();
        // 过滤出此次需要转换的信息
        List<ConverterFieldInfo> alternativeConvertInfoList = alternativeConvertInfoList(converterAnnotationClasses);
        Set<String> messageSet = new HashSet<>();
        for (ConverterFieldInfo converterFieldInfo : alternativeConvertInfoList) {
            long itemBeforeTime = System.currentTimeMillis();
            try {
                String sourceFieldName = converterFieldInfo.getSourceFieldName();
                String sourceFieldGetterName = ReflectHelper.field2getter(sourceFieldName);
                Method sourceFieldGetter = sourceClass.getMethod(sourceFieldGetterName);
                List sourceValueList=new ArrayList();
                for (S source : sourceList) {
                    Object sourceValue = sourceFieldGetter.invoke(source);
                    if (sourceValue!=null){
                        sourceValueList.add(sourceValue);
                    }
                }

                // 封装 sourceList 和 targetList
                ConvertListContext convertListContext = new ConvertListContext<>();
                convertListContext.setAttach(attach);
                convertListContext.setConverterAnnotation(converterFieldInfo.getConverterAnnotation());
                convertListContext.setSourceValueList(sourceValueList);

                // 获取target处理器
                AbstractConverter converter = converterFieldInfo.getConverter();
                Function<ConvertContext,?> targetHandle = converter.convertList(convertListContext);
                if (targetHandle==null){
                    continue;
                }

                for (int i = 0; i < targetList.size(); i++) {
                    S source = sourceList.get(i);
                    T target = targetList.get(i);
                    Object sourceValue = sourceFieldGetter.invoke(source);

                    // 封装 convertContext
                    ConvertContext convertContext = new ConvertContext<>();
                    convertContext.setAttach(attach);
                    convertContext.setConverterAnnotation(converterFieldInfo.getConverterAnnotation());
                    convertContext.setSourceValue(sourceValue);

                    // 执行转换
                    Object targetFieldValue = targetHandle.apply(convertContext);
                    // 对target设置结果值
                    setTargetValue(target,targetFieldValue, converterFieldInfo);
                    // 处理attach，保证此次转换一直能拿到这个 attach
                    attach=convertContext.getAttach();
                }
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ConvertTypeException e) {
                messageSet.add(e.getMessage());
            }
            long itemAfterTime = System.currentTimeMillis();
            long executeTime=(itemAfterTime- itemBeforeTime);
            System.out.println("执行转换 [ "+converterFieldInfo.getConverterClass().getSimpleName()+" ] , 消耗时间："+executeTime);
        }
        for (String message : messageSet) {
            logger.info(message);
        }
        long afterTime = System.currentTimeMillis();
        long executeTime=(afterTime- beforeTime);
        System.out.println("执行转换 [ "+targetClass.getSimpleName()+" ] , 总消耗时间："+executeTime);
        return targetList;
    }

    /**
     * 此次需要转换的 ConverterFieldInfo
     * @param converterAnnotationClasses
     * @return
     */
    private List<ConverterFieldInfo> alternativeConvertInfoList(Class<?>... converterAnnotationClasses) {
        List<ConverterFieldInfo> res = new ArrayList<>();
        for (Class<?> converterAnnotationClass : converterAnnotationClasses) {
            for (ConverterFieldInfo converterFieldInfo : cacheConverterFieldInfoList) {
                Class<?> candidate = converterFieldInfo.getConverterAnnotationClass();
                if (candidate.equals(converterAnnotationClass)) {
                    res.add(converterFieldInfo);
                    // 目标类可能有多个重复的converter注解，因此不能 break;
                }
            }
        }
        return res;
    }

    /**
     * 设置属性
     * @param targetFieldValue
     */
    private void setTargetValue(Object target,Object targetFieldValue, ConverterFieldInfo converterFieldInfo) throws ConvertTypeException, InvocationTargetException, IllegalAccessException {
        if (targetFieldValue == null||target==null) {
            return;
        }

        Method targetFieldSetter = converterFieldInfo.getTargetFieldSetter();
        Field targetField = converterFieldInfo.getTargetField();

        Class<?> targetFieldValueClass = targetFieldValue.getClass();
        Class<?> realTargetFieldClass = targetField.getType();
        // 验证 需要的参数类型和 convert 结果数据类型是否匹配
        if (!realTargetFieldClass.isAssignableFrom(targetFieldValueClass)) {
            throw new ConvertTypeException("need ["+realTargetFieldClass.getSimpleName()+"] but had ["+targetFieldValueClass.getSimpleName()+"]");
        }
        targetFieldSetter.invoke(target, targetFieldValue);
    }

    /**
     * 初始化，将targetClass中的所有convert注解字段缓存为convertInfo
     */
    private void init() {
        ReflectHelper.processFields(targetClass, targetField -> {
            Annotation[] fieldAnnotations = targetField.getAnnotations();
            // 如果属性没有注解或者没有setter直接返回
            if (fieldAnnotations == null) {
                return;
            }
            String fieldName = targetField.getName();
            Method targetSetter;
            try {
                targetSetter = ReflectHelper.getSetter(targetClass, fieldName);
            } catch (NoSuchMethodException | NoSuchFieldException | ConvertParamException e) {
                logger.info(e.getClass().getSimpleName() + " : " + e.getMessage());
                return;
            }
            for (Annotation fieldAnnotation : fieldAnnotations) {
                Class<? extends Annotation> annotationType = fieldAnnotation.annotationType();
                // 此注解没有Transform表示非转换注解
                if (!annotationType.isAnnotationPresent(Transform.class)){
                    continue;
                }
                // convert注解必须有sourceField属性，并且必须为String类型
                String sourceFieldName;
                try {
                    Method getSourceFieldMethod = annotationType.getMethod(ConvertConstant.CONVERT_ANNOTATION_FIELD_SOURCE_FIELD);
                    Object sourceFieldNameObj = getSourceFieldMethod.invoke(fieldAnnotation);
                    if (!(sourceFieldNameObj instanceof String)) {
                        logger.info(annotationType.getSimpleName() + " has no [ String ] type of [ " + ConvertConstant.CONVERT_ANNOTATION_FIELD_SOURCE_FIELD + " ]");
                        continue;
                    }
                    sourceFieldName = (String) sourceFieldNameObj;
                    if (StringUtils.isEmpty(sourceFieldName)) {
                        logger.info(annotationType.getSimpleName() + " [ " + ConvertConstant.CONVERT_ANNOTATION_FIELD_SOURCE_FIELD + " ] could not blank");
                        continue;
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    logger.info(e.getClass().getSimpleName() + " : " + e.getMessage());
                    continue;
                }
                // converter注解中Transform的converter，必须也带converter注解类型的泛型
                Transform transformAnnotation = annotationType.getAnnotation(Transform.class);
                Class<? extends AbstractConverter<? extends Annotation, ?, ?>> converterClass = transformAnnotation.converter();
                Class<?> realConverterAnnotationClass = ReflectHelper.getSuperOneGeneric(converterClass);
                if ((realConverterAnnotationClass == null) || (!realConverterAnnotationClass.equals(annotationType))) {
                    logger.severe(converterClass.getName() + " has no generic named [ " + annotationType.getName() + " ] ");
                    continue;
                }
                // 封装ConvertInfo
                try {
                    AbstractConverter converter = ReflectHelper.newInstance(converterClass);

                    ConverterFieldInfo<Annotation, Object, Object> converterFieldInfo = new ConverterFieldInfo<>();
                    converterFieldInfo.setConverterAnnotationClass(realConverterAnnotationClass);
                    converterFieldInfo.setConverterAnnotation(fieldAnnotation);

                    converterFieldInfo.setConverter(converter);
                    converterFieldInfo.setConverterClass(converterClass);

                    converterFieldInfo.setTargetField(targetField);
                    converterFieldInfo.setTargetFieldSetter(targetSetter);
                    converterFieldInfo.setSourceFieldName(sourceFieldName);

                    // 缓存
                    cacheConverterFieldInfoList.add(converterFieldInfo);
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    logger.severe(e.getMessage());
                }
            }
        });
    }
}
